// Tierless
// Copyright © 2010 Alphora
// This file is licensed under a modified BSD-license (see tierless_license.txt).
using System;
using System.Runtime.Serialization;

namespace Tierless.Framework.Constraints.Common
{
	/// <summary>
	/// Enforces that a decimal value is within a given precision and scale, as supported by most data platforms.
	/// </summary>
	[DataContract]
	public class DecimalValueValid : ColumnConstraint
	{
		/// <summary>
		/// Constructs a new instance of the DecimalValueValid constraint.
		/// </summary>
		/// <param name="entityTypeName">The name of the entity type on which the constraint will be placed.</param>
		/// <param name="columnName">The name of the column on which the constraint will be placed.</param>
		/// <param name="precision">The number of total digits allowed in the decimal value.</param>
		/// <param name="scale">The number of digits allowed after the decimal, 0 for integral values.</param>
		public DecimalValueValid(string entityTypeName, string columnName, int precision, int scale)
			: base
			(
				String.Format("{0}_{1}DecimalValueValid", entityTypeName, columnName),
				entityTypeName,
				columnName,
				scale > 0
					? String.Format("Value must have no more than ({0}) total digits, ({1}) of which are after the decimal point.", precision, scale)
					: String.Format("Value must have no more than ({0}) digits and cannot have a decimal point.", precision)
			)
		{
			_scalingFactor = Convert.ToDecimal(Math.Pow(10, scale));
			_maxValue = (Convert.ToDecimal(Math.Pow(10, precision)) - 1) / _scalingFactor;
			_minValue = -(_maxValue);
		}

		[DataMember]
		private decimal _minValue;

		[DataMember]
		private decimal _maxValue;

		[DataMember]
		private decimal _scalingFactor;

		protected override bool IsValid(ConstraintContext context)
		{
			var decimalValue = context.Entity[ColumnName].AsDecimal;
			var scaledValue = decimalValue * _scalingFactor;
			return (decimalValue >= _minValue) && (decimalValue <= _maxValue) && ((int)Math.Floor((double)Math.Abs(scaledValue)) == Math.Abs(scaledValue));
		}
	}
}